#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <util/delay.h>
#include <stdint.h>
#include "common.h"
#include "delay_util.h"
#include "main.h"
#include "descriptors.h"
#include "led.h"
#include "CDCClassDevice.h"
#include "pn.h"

#include "klPrintf_USB.h"

// Prototypes
void GeneralInit(void);
void EVENT_CardAppeared(void);

// Clock division
typedef enum {
    clock_div_1 = 0,
    clock_div_2 = 1,
    clock_div_4 = 2,
    clock_div_8 = 3,
    clock_div_16 = 4,
    clock_div_32 = 5,
    clock_div_64 = 6,
    clock_div_128 = 7,
    clock_div_256 = 8,
} clock_div_t;
// Set the clock prescaler register select bits, selecting a system clock division setting. They type of x is clock_div_t.
#define clock_prescale_set(x) \
do { \
    uint8_t tmp = _BV(CLKPCE); \
    __asm__ __volatile__ ( \
        "in __tmp_reg__,__SREG__" "\n\t" \
        "cli" "\n\t" \
        "sts %1, %0" "\n\t" \
        "sts %1, %2" "\n\t" \
        "out __SREG__, __tmp_reg__" \
        : /* no outputs */ \
        : "d" (tmp), \
          "M" (_SFR_MEM_ADDR(CLKPR)), \
          "d" (x) \
        : "r0"); \
} while (0)

// ============================== Implementation ==============================
int main(void) {
    GeneralInit();

    uint16_t Tmr;
    // Main cycle
    while(1) {

        USB_USBTask();
        Led.Task();
        CDC_Device_ReceiveByte();
        CDC_Device_USBTask();

        Pn.Task();

        if(DelayElapsed(&Tmr, 999)) {
//            klPrintf("ogo\r");
//            if(Pn.Cmd(0x02, 0) == pnPktOk) klPrintf("get frm: %A\r", Pn.Buf, Pn.Length);
        }
    } // while 1
    return 0;
}

FORCE_INLINE void GeneralInit(void) {
    _delay_ms(99);
    clock_prescale_set(clock_div_1);    // Disable clock division

    Led.Init();
    DelayInit();
    USB_Init();

    Pn.Init();
    Pn.Evt_CardAppeared = EVENT_CardAppeared;

    sei();
    klPrintf("\rAvrComReader\r");
}

void EVENT_CardAppeared(void) {
    if (!Card.ReadID()) return;
    klPrintf("ID:");
    for(uint8_t i=0; i<7; i++) klPrintf("%X ", Card.ID[i]);
    Led.Blink();
    klPrintf("\r\n");
}

// ========================== USB events handlers ==============================
/** Event handler for the USB_Connect event. This indicates that the device is enumerating via the status LEDs and
 *  starts the library USB task to begin the enumeration and USB management process. */
void EVENT_USB_Device_Connect() {
    Led.On();
}

/** Event handler for the USB_Disconnect event. This indicates that the device is no longer connected to a host via
 *  the status LEDs and stops the USB management and CDC management tasks. */
void EVENT_USB_Device_Disconnect() {
    Led.Off();
}

/** Event handler for the USB_ConfigurationChanged event. This is fired when the host set the current configuration
 *  of the USB device after enumeration - the device endpoints are configured and the CDC management task started. */
void EVENT_USB_Device_ConfigurationChanged() {
    // Setup Endpoints
    Endpoint_ConfigureEndpoint(CDC_RX_EPNUM, EP_TYPE_BULK, ENDPOINT_DIR_OUT, CDC_TXRX_EPSIZE, ENDPOINT_BANK_SINGLE);
    Endpoint_ConfigureEndpoint(CDC_TX_EPNUM, EP_TYPE_BULK, ENDPOINT_DIR_IN,  CDC_TXRX_EPSIZE, ENDPOINT_BANK_SINGLE);
    Endpoint_ConfigureEndpoint(CDC_NOTIFICATION_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,  CDC_NOTIFICATION_EPSIZE, ENDPOINT_BANK_SINGLE);
}

/** Event handler for the library USB Control Request reception event. */
void EVENT_USB_Device_ControlRequest(void) {
    CDC_Device_ProcessControlRequest();
}
